package org.infodavid.common.ejb;

import java.util.Locale;
import java.util.Map;

import javax.ejb.Local;
import javax.ejb.PrePassivate;
import javax.ejb.Remote;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;

import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.Persister;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

import org.infodavid.common.dto.IUserListItem;
import org.infodavid.common.ejb.components.ImageDataComponent;
import org.infodavid.common.impl.model.TemplateVersionImpl;
import org.infodavid.common.model.IImageData;
import org.infodavid.common.model.ITemplate;
import org.infodavid.common.model.ITemplateCategory;
import org.infodavid.common.model.ITemplateVersion;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.IVersionObject;
import org.infodavid.common.model.components.IOwnerDetails;
import org.infodavid.common.model.impl.persisters.VersionSafeSingleTableEntityPersister;
import org.infodavid.common.persistence.ICommented;
import org.infodavid.common.persistence.INamed;
import org.infodavid.common.persistence.TemplateVersionDataService;

/**
 * The Class TemplateVersionBean.
 */
@Entity(name = "ITemplateVersion")
@org.hibernate.annotations.Entity(dynamicUpdate = true, selectBeforeUpdate = true, dynamicInsert = true, optimisticLock = OptimisticLockType.VERSION)
@Persister(impl = VersionSafeSingleTableEntityPersister.class)
@Table(name = "TEMPLATE_VERSIONS", uniqueConstraints = {
	@UniqueConstraint(name = "UC_TEMPLATE_VERSIONS_TEMPLATE_REVISION", columnNames = {
	    ITemplate.ID, ITemplateVersion.REVISION
	})
})
@NamedQueries(value = {
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_COUNT, query = "select count(d."
        + ITemplateVersion.KEY + ") from ITemplateVersion d"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND, query = "select d from ITemplateVersion d"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_BY_CREATION_DATE, query = "select d from ITemplateVersion d where d.archived = 0 and d."
        + ITemplateVersion.CREATION_DATE + " between :date1 and :date2"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_BY_CREATION_DATE_COUNT, query = "select count(d."
        + ITemplateVersion.KEY
        + ") from ITemplateVersion d where d.archived = 0 and d."
        + ITemplateVersion.CREATION_DATE + " between :date1 and :date2"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_BY_MODIFICATION_DATE, query = "select d from ITemplateVersion d where d.archived = 0 and d."
        + ITemplateVersion.MODIFICATION_DATE + " between :date1 and :date2"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_BY_MODIFICATION_DATE_COUNT, query = "select count(d."
        + ITemplateVersion.KEY
        + ") from ITemplateVersion d where d.archived = 0 and d."
        + ITemplateVersion.MODIFICATION_DATE + " between :date1 and :date2"),
    @NamedQuery(name = TemplateVersionDataService.QUERY_FIND_LAST, query = "select d from ITemplateVersion d where d.archived = 0 and d."
        + ITemplate.TEMPLATE
        + '.'
        + ITemplateVersion.KEY
        + " =  :"
        + ITemplate.ID
        + " and d."
        + ITemplateVersion.REVISION
        + " = (select max(d."
        + ITemplateVersion.REVISION
        + ") from ITemplateVersion d where d.archived = 0 and d."
        + ITemplate.TEMPLATE
        + " =  :"
        + ITemplate.ID + ')')
})
@SqlResultSetMappings({
    @SqlResultSetMapping(name = "ITemplateVersion.revision", columns = @ColumnResult(name = "rev")),
    @SqlResultSetMapping(name = "ITemplateVersion.implicit", entities = @EntityResult(entityClass = TemplateVersionBean.class))
})
@NamedNativeQueries(value = {
    @NamedNativeQuery(name = TemplateVersionDataService.QUERY_FIND_LAST_REVISION, query = "select max(dv."
        + ITemplateVersion.REVISION
        + ") as rev from TEMPLATE_VERSIONS dv where dv.archived = 0 and dv."
        + ITemplate.ID
        + " = :" + ITemplate.ID, resultSetMapping = "ITemplateVersion.revision"),
    @NamedNativeQuery(name = TemplateVersionDataService.QUERY_FIND_LAST_BY_NAME, query = "select * from TEMPLATE_VERSIONS dv, (select max(dv."
        + IVersionObject.REVISION
        + ") as rev, seldoc.id as id from TEMPLATE_VERSIONS dv join (select doc."
        + ITemplate.ID
        + " as id from TEMPLATES doc where doc.archived = 0 and doc."
        + ITemplate.NAME
        + " = :"
        + ITemplate.NAME
        + " and doc."
        + ITemplate.LANGUAGE
        + " = :"
        + ITemplate.LANGUAGE
        + ") seldoc on seldoc.id = dv."
        + ITemplate.ID
        + " group by seldoc.id) seldv where dv.archived = 0 and seldv.id = dv."
        + ITemplate.ID
        + " and dv." + IVersionObject.REVISION + " = seldv.rev;", resultSetMapping = "ITemplateVersion.implicit")
})
@Local(value = ITemplateVersion.class)
@Remote(value = ITemplateVersionRemote.class)
public class TemplateVersionBean extends TemplateVersionImpl {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1660345027625851665L;

	/**
	 * The Constructor.
	 * @throws Exception the exception
	 */
	protected TemplateVersionBean() throws Exception {
		super();
	}

	/**
	 * The Constructor.
	 * @param keyArg the key arg
	 * @throws Exception the exception
	 */
	protected TemplateVersionBean(final Long keyArg) throws Exception {
		super(keyArg);
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#init()
	 */
	@Override
	@PrePassivate
	public void init() throws Exception {
		super.init();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#getCreationDate()
	 */
	@Override
	@Column(name = CREATION_DATE, nullable = false, updatable = false)
	@Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
	@Index(name = "IDX_TEMPLATE_VERSIONS_CREATIONDATE")
	public DateTime getCreationDate() {
		return super.getCreationDate();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#getModificationDate()
	 */
	@Override
	@Version
	@Column(name = MODIFICATION_DATE, nullable = false)
	@Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
	public DateTime getModificationDate() {
		return super.getModificationDate();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#getKey()
	 */
	@Override
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = ID, nullable = false)
	public Long getKey() {
		return super.getKey();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#isArchived()
	 */
	@Override
	@Column(name = ARCHIVED, nullable = false)
	@Index(name = "IDX_TEMPLATE_VERSIONS_ARCHIVED")
	public boolean isArchived() {
		return super.isArchived();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getAttachments()
	 */
	@Override
	@ElementCollection(fetch = FetchType.LAZY, targetClass = ImageDataComponent.class)
	@CollectionTable(name = "TEMPLATE_VERSIONS_IMAGES", joinColumns = @JoinColumn(name = ID, nullable = false), uniqueConstraints = @UniqueConstraint(name = "UC_TEMPLATE_VERSIONS_IMAGES", columnNames = {
	    INamed.NAME, ID
	}))
	@MapKeyColumn(name = INamed.NAME, nullable = false, length = 128)
	@ForeignKey(name = "FK_TEMPLATE_VERSIONS_IMAGES")
	public Map<String,IImageData> getImages() {
		return super.getImages();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getData()
	 */
	@Override
	@Lob
	@Basic(fetch = FetchType.LAZY)
	@Column(name = DATA, nullable = true, length = 1073741824, updatable = false)
	public byte[] getData() {
		return super.getData();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getEncoding()
	 */
	@Override
	@Column(name = ENCODING, length = 32, nullable = true, unique = false, updatable = false)
	public String getEncoding() {
		return super.getEncoding();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getLanguage()
	 */
	@Override
	@Transient
	public Locale getLanguage() {
		return super.getLanguage();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getMimeType()
	 */
	@Override
	@Transient
	public String getMimeType() {
		return super.getMimeType();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getOwnerDetails()
	 */
	@Override
	@Transient
	public IOwnerDetails getOwnerDetails() {
		return super.getOwnerDetails();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getSubject()
	 */
	@Override
	@Column(name = SUBJECT, length = 256, nullable = true, unique = false, updatable = true)
	@Index(name = "IDX_TEMPLATE_VERSIONS_SUBJECT")
	public String getSubject() {
		return super.getSubject();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getName()
	 */
	@Override
	@Transient
	public String getName() {
		return super.getName();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getCategory()
	 */
	@Override
	@JoinColumn(name = ITemplateCategory.ID, referencedColumnName = ITemplateCategory.ID, nullable = true)
	@ManyToOne(targetEntity = TemplateCategoryBean.class, fetch = FetchType.LAZY)
	@ForeignKey(name = "FK_TEMPLATE_VERSIONS_CATEGORY")
	public ITemplateCategory getCategory() {
		return super.getCategory();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getTemplate()
	 */
	@Override
	@JoinColumn(name = ITemplate.ID, referencedColumnName = ITemplate.ID, nullable = false, updatable = false)
	@ManyToOne(targetEntity = TemplateBean.class, fetch = FetchType.LAZY)
	@ForeignKey(name = "FK_TEMPLATE_VERSIONS_TEMPLATE")
	@OnDelete(action = OnDeleteAction.CASCADE)
	public ITemplate getTemplate() {
		return super.getTemplate();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getComment()
	 */
	@Override
	@Column(name = ICommented.COMMENT, length = 1024, nullable = true, updatable = true)
	public String getComment() {
		return super.getComment();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getReference()
	 */
	@Override
	@Column(name = REFERENCE, nullable = true, updatable = false)
	@Index(name = "IDX_TEMPLATE_VERSIONS_REFERENCE")
	public Integer getReference() {
		return super.getReference();
	}

	/**
	 * Prepare the storage.
	 */
	@PrePersist
	@PreUpdate
	protected void preStore() {
		if (getRevision() <= 0) {
			setRevision(1);
		}

		if (getData() != null) {
			setReference(null);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getRevision()
	 */
	@Override
	@Column(name = REVISION, nullable = false, updatable = false)
	@Index(name = "IDX_TEMPLATE_VERSIONS_REVISION")
	public int getRevision() {
		return super.getRevision();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.impl.model.TemplateVersionImpl#getUser()
	 */
	@Override
	@JoinColumn(name = IUser.ID, referencedColumnName = IUser.ID, nullable = true)
	@ManyToOne(targetEntity = UserBean.class, fetch = FetchType.LAZY)
	@ForeignKey(name = "FK_TEMPLATE_VERSIONS_USER")
	public IUserListItem getUser() {
		return super.getUser();
	}

	/**
	 * Prepare the removal.
	 */
	@PreRemove
	protected void preRemove() {
		setTemplate(null);
		setCategory(null);
	}
}
